﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using JetBrains.Annotations;

namespace CxExtension
{
	public static class DictionaryExtension
	{
		public static void ToList<K, V, T>(this IDictionary<K, V> self,IList<T> list, Func<KeyValuePair<K, V>, T> convertFunc)
		{
			foreach (var it in self)
			{
				var t = convertFunc(it);
				list.Add(t);
			}
		}

		public static List<V> ToListValue<K, V>(this IDictionary<K, V> self)
		{
			var list = new List<V>(self.Count);
			self.ToList(list,(it => it.Value));
			return list;
		}
		public static bool MoveTo<K, V>(this IDictionary<K, V> self, K key, IDictionary<K, V> target)
		{
			V value;
			var has = self.TryGetValue(key, out value);
			if (has)
			{
				target.Add(key, value);
			}
			return has;
		}

		public static Dictionary<K, V1> ConvertAll<K, V, V1>(this IDictionary<K, V> self, Func<V, V1> func)
		{
			var dic = new Dictionary<K, V1>();
			foreach (var item in self)
			{
				dic.Add(item.Key, func(item.Value));
			}
			return dic;
		}
		/// <summary>
		/// 查找所有符合条件的
		/// </summary>
		/// <typeparam name="K"></typeparam>
		/// <typeparam name="V"></typeparam>
		/// <param name="self"></param>
		/// <param name="func"></param>
		/// <returns></returns>
		public static Dictionary<K, V> FindAll<K, V>(this IDictionary<K, V> self, Func<K, V, bool> func)
		{
			var dic = new Dictionary<K, V>();
			FindAll(self, dic, func);
			return dic;
		}
		/// <summary>
		/// find all and add to dic
		/// </summary>
		/// <typeparam name="K"></typeparam>
		/// <typeparam name="V"></typeparam>
		/// <param name="self"></param>
		/// <param name="dic"></param>
		/// <param name="func"></param>
		public static void FindAll<K, V>(this IDictionary<K, V> self, IDictionary<K, V> dic, Func<K, V, bool> func)
		{
			foreach (var item in self)
			{
				if (func(item.Key, item.Value))
				{
					dic.Add(item.Key, item.Value);
				}
			}
		}
		/// <summary>
		/// find all and add to list
		/// </summary>
		/// <typeparam name="K"></typeparam>
		/// <typeparam name="V"></typeparam>
		/// <param name="self"></param>
		/// <param name="list"></param>
		/// <param name="func"></param>
		public static void FindAll<K, V>(this IDictionary<K, V> self, ICollection<V> list, Func<K, V, bool> func)
		{
			foreach (var item in self)
			{
				if (func(item.Key, item.Value))
				{
					list.Add(item.Value);
				}
			}
		}
		/// <summary>
		/// check value can replace the key value
		/// </summary>
		/// <typeparam name="K"></typeparam>
		/// <typeparam name="V"></typeparam>
		/// <param name="self"></param>
		/// <param name="key"></param>
		/// <param name="value"></param>
		/// <param name="compareFunc"></param>
		/// <returns>if true the value can replace key,s value</returns>
		public static bool NeedReplace<K, V>(this IDictionary<K, V> self, K key, V value, Func<V, V, int> compareFunc = null)
		{
			var state = self.CheckState(key, value, compareFunc);
			return state == 0 || state == 2;
		}

		public static int CheckState<K, V>(this IDictionary<K, V> self, K key, V value)
		{
			return self.CheckState(key, value, null);
		}
		/// <summary>
		/// 检查指定key在map中的状态.
		/// </summary>
		/// <typeparam name="K"></typeparam>
		/// <typeparam name="V"></typeparam>
		/// <param name="self"></param>
		/// <param name="key"></param>
		/// <param name="value"></param>
		/// <param name="compareFunc"> if null then use V type IComparer V replace</param>
		/// <returns>
		/// 0 没有,
		/// 1 有,
		/// 2 update</returns>
		public static int CheckState<K, V>(this IDictionary<K, V> self, K key, V value, Func<V, V, int> compareFunc)
		{
			if (compareFunc == null)
			{
				var com = (IComparer<V>)value;
				if (com != null)
				{
					compareFunc = com.Compare;
				}
			}
			if (compareFunc == null) throw new ArgumentNullException("compareFunc");

			V v;
			var state = 0;
			if (self.TryGetValue(key, out v))
			{
				var res = compareFunc(v, value);
				state = res >= 0 ? 1 : 2;
			}

			return state;
		}
		/// <summary>
		/// 是否用新的元素覆盖
		/// </summary>
		/// <typeparam name="K"></typeparam>
		/// <typeparam name="V"></typeparam>
		/// <param name="self"></param>
		/// <param name="key"></param>
		/// <param name="value"></param>
		/// <param name="compareFunc"></param>
		/// <returns></returns>
		public static bool Override<K, V>(this IDictionary<K, V> self, K key, V value, Func<V, V, int> compareFunc)
		{
			var needAdd = self.NeedReplace(key, value, compareFunc);

			if (needAdd)
			{
				self[key] = value;
			}

			return needAdd;
		}

		/// <summary>
		/// 添加某个字典的键值到当前字典
		/// </summary>
		/// <typeparam name="K"></typeparam>
		/// <typeparam name="V"></typeparam>
		/// <param name="self"></param>
		/// <param name="src"></param>
		/// <param name="isOverride">是否覆盖已有的value</param>
		public static void Add<K, V>(this IDictionary<K, V> self, IDictionary<K, V> src, bool isOverride = true)
		{
			foreach (var item in src)
			{
				if (isOverride || !self.ContainsKey(item.Key))
				{
					self[item.Key] = item.Value;
				}
			}
		}
		public static V GetOrNewAdd<K, V>(this IDictionary<K, V> self, K k) where V : class, new()
		{

			return self.GetOrNewAdd(k, () => new V());
		}
		/// <summary>
		/// 获取字典里是否存在某个键值,如果存在就返回,如果不存在 就new一个添加进去并返回
		/// </summary>
		/// <typeparam name="K"></typeparam>
		/// <typeparam name="V"></typeparam>
		/// <param name="self"></param>
		/// <param name="k"></param>
		/// <param name="newfunc"></param>
		/// <returns></returns>
		public static V GetOrNewAdd<K, V>(this IDictionary<K, V> self, K k, Func<V> newfunc) where V : class, new()
		{
			return self.GetOrNewAdd(k, (key) => newfunc());
		}

		public static V GetOrNewAdd<K, V>(this IDictionary<K, V> self, K k, [NotNull] Func<K, V> newfunc)
			where V : new()
		{
			if (newfunc == null)
			{
				newfunc = it => new V();
			}
			return self.GetOrNewAddF(k, newfunc);
		}

		public static V GetOrNewAddF<K, V>(this IDictionary<K, V> self, K k, [NotNull] Func<K, V> newfunc)
		{
			return self.GetOrNewAddF(k, newfunc, true);
		}
		public static V GetOrNewAddF<K, V>(this IDictionary<K, V> self, K k, [NotNull] Func<K, V> newfunc,bool addDir)
		{
			if (newfunc == null) throw new ArgumentNullException("newfunc");
			V v;
			if (self.TryGetValue(k, out v))
			{
				return v;
			}

			v = newfunc(k);
			if (v == null) return default(V);
			if (addDir)
			{
				self.Add(k, v);
			}
			return v;
		}
	}
}
